package com.easylinkin.linkappapi.reportCenter.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.lobar.mapper.UserStatisticsMapper;
import com.easylinkin.linkappapi.reportCenter.constant.AlarmEnum;
import com.easylinkin.linkappapi.reportCenter.entity.*;
import com.easylinkin.linkappapi.reportCenter.entity.dto.ReportCountDTO;
import com.easylinkin.linkappapi.reportCenter.entity.dto.ReportDetailDTO;
import com.easylinkin.linkappapi.reportCenter.entity.vo.*;
import com.easylinkin.linkappapi.reportCenter.mapper.*;
import com.easylinkin.linkappapi.reportCenter.service.DeviceReportService;
import com.easylinkin.linkappapi.reportCenter.service.ReportAiService;
import com.easylinkin.linkappapi.reportCenter.service.ReportElectricalService;
import com.easylinkin.linkappapi.reportCenter.service.ReportMachineryService;
import com.easylinkin.linkappapi.reportCenter.timer.DeviceReportTimer;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.date.DatePattern.NORM_MONTH_PATTERN;

/**
 * @Description 设备运行报告
 * @Author kan yuanfeng
 * @Date 2023/7/13 14:17
 */
@Service
@Slf4j
public class DeviceReportServiceImpl implements DeviceReportService {
    @Autowired
    private LinkappUserContextProducer userContextProducer;
    @Autowired
    private ReportMapper reportMapper;
    @Autowired
    private ReportDeviceMapper reportDeviceMapper;
    @Autowired
    private ReportAlarmMapper reportAlarmMapper;
    @Autowired
    private ReportEnergyMapper reportEnergyMapper;
    @Autowired
    private ReportSprayMapper reportSprayMapper;
    @Autowired
    private ReportAiService reportAiService;
    @Autowired
    private ReportMachineryService reportMachineryService;
    @Autowired
    private ReportElectricalService reportElectricalService;
    @Autowired
    private UserStatisticsMapper userStatisticsMapper;
    @Autowired
    private DeviceReportTimer deviceReportTimer;
    @Autowired
    private DeviceMapper deviceMapper;
    /**
     * @Description: 根据条件，分页(不分页)查询报表
     * @author kan yuanfeng
     * @date 2020/11/04 11:42
     */
    @Override
    public IPage<Report> findReportList(RequestModel<Report> requestModel) {
        Page page = requestModel.getPage();
        Report report = requestModel.getCustomQueryParams();
        QueryWrapper<Report> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("tenant_id_",userContextProducer.getTenantId());
        queryWrapper.eq(null != report.getId(),"id",report.getId());
        queryWrapper.like(StringUtils.isNotBlank(report.getName()),"name_",report.getName());
        queryWrapper.orderByDesc("create_time_");
        IPage iPage = reportMapper.selectPage(page, queryWrapper);
        return iPage;
    }

    /**
     * @Description: 运行报告设备统计
     * @author kan yuanfeng
     * @date 2023/07/14 11:42
     */
    @Override
    public List<ReportDeviceVo> findDevice(Integer reportId) {
        List<ReportDeviceVo> reportDeviceVos = reportDeviceMapper.findDevice(reportId);
        return reportDeviceVos;
    }

    /**
     * @Description: 运行报告告警统计
     * @author kan yuanfeng
     * @date 2023/07/14 11:42
     */
    @Override
    public Map<String, Object> findAlarm(Integer reportId) {
        Report report = reportMapper.selectById(reportId);
        Map<String, Object> map = new HashMap<>();
        //设备总数
        List<ReportDeviceVo> reportDeviceVos = this.findDevice(reportId);
        int sum = reportDeviceVos.stream().mapToInt(r -> r.getNum()).sum();
        map.put("deviceNum",sum);
        //查询本月数据
        QueryWrapper<ReportAlarm> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("report_id_",reportId);
        List<ReportAlarm> curAlarms = reportAlarmMapper.selectList(queryWrapper);
        //查询上月数据
        Report last = reportMapper.findLast(reportId);
        List<ReportAlarm> lastAlarms;
        if (null != last){
            QueryWrapper<ReportAlarm> lastWrapper = new QueryWrapper<>();
            lastWrapper.eq("report_id_",last.getId());
            lastAlarms = reportAlarmMapper.selectList(lastWrapper);
        }else {
            //实时计算
            lastAlarms = deviceReportTimer.countAlram(report.getTenantId(), DateUtil.offsetMonth(report.getStartTime(), -1), null);
        }
        List<Integer> types = new ArrayList<>();
        curAlarms.forEach(c-> {
            if (!types.contains(c.getType())){
                types.add(c.getType());
            }
        });
        lastAlarms.forEach(c-> {
            if (!types.contains(c.getType())){
                types.add(c.getType());
            }
        });
        List<ReportCountDTO<Integer>> reportCountDTOS = new ArrayList<>();
        types.forEach(type ->{
            String name = AlarmEnum.getByType(type).getName();
            //本月
            int curSum = curAlarms.stream().filter(c -> type.equals(c.getType())).mapToInt(c -> c.getNum()).sum();
            //上月
            int lastSum = lastAlarms.stream().filter(c -> type.equals(c.getType())).mapToInt(c -> c.getNum()).sum();
            ReportCountDTO<Integer> reportCountDTO = new ReportCountDTO<>(name,curSum,lastSum);
            reportCountDTOS.add(reportCountDTO);
        });
        //本月总计
        map.put("currentMonth",reportCountDTOS.stream().mapToInt(r->r.getCurrentMonth()).sum());
        //上月总计
        map.put("lastMonth",reportCountDTOS.stream().mapToInt(r->r.getLastMonth()).sum());
        List<ReportCountDTO<Integer>> dtos = reportCountDTOS.stream().sorted(Comparator.comparing(ReportCountDTO::getCurrentMonth)).collect(Collectors.toList());
        Collections.reverse(dtos);
        map.put("alarm",dtos);
        return map;
    }

    /**
     * 绿色施工查询
     */
    @Override
    public List<ReportDetailDTO> findEnergy(Integer reportId) {
        Report report = reportMapper.selectById(reportId);
        //项目平均在场人数
        Integer userNum = getUserNum(reportId);
        int avgUserNum = avgUserNum(report.getStartTime(), userNum);

        List<ReportDetailDTO> result = new ArrayList<>();
        //用水/用电
        QueryWrapper<ReportEnergy> curWrapper = new QueryWrapper<>();
        curWrapper.eq("report_id_",reportId);
        List<ReportEnergy> energies = reportEnergyMapper.selectList(curWrapper);
        if (CollectionUtil.isNotEmpty(energies)){
            //查询上个月的数据
            Report last = reportMapper.findLast(reportId);
            Integer lastUserNum = 0;
            if (null != last){
                //上个月项目场均人数
                lastUserNum = getUserNum(last.getId());
            }else {
                //实时计算
                lastUserNum = userStatisticsMapper.getUserNumByTime(report.getTenantId(),DateUtil.offsetMonth(report.getStartTime(),-1));
            }
            int lastAvgUserNum = avgUserNum(DateUtil.offsetMonth(report.getStartTime(),-1), lastUserNum);


            List<ReportEnergy> lastEnergyList = new ArrayList<>();
            if (null != last){
                //处理水/电
                QueryWrapper<ReportEnergy> lastWrapper = new QueryWrapper<>();
                lastWrapper.eq("report_id_",last.getId());
                lastEnergyList = reportEnergyMapper.selectList(lastWrapper);
            }else {
                //实时计算
                lastEnergyList.addAll(deviceReportTimer.countEnergy(report.getTenantId(), DateUtil.offsetMonth(report.getStartTime(), -1), 1,null));
                lastEnergyList.addAll(deviceReportTimer.countEnergy(report.getTenantId(), DateUtil.offsetMonth(report.getStartTime(), -1), 2,null));
            }

            //用水处理
            energyHandler(avgUserNum, lastAvgUserNum, energies, lastEnergyList, 1,result);
            //用电处理
            energyHandler(avgUserNum, lastAvgUserNum, energies, lastEnergyList, 2,result);
        }
        //喷淋控制
        sprayHandler(reportId, result);
        return result;
    }

    /**
     * 求平均人数
     * @param date
     */
    private static int avgUserNum(Date date,Integer userNum) {
        //求平均在场人数
        String format = DateUtil.format(date, NORM_MONTH_PATTERN);
        String[] split = format.split("-");
        int lengthOfMonth = DateUtil
                .lengthOfMonth(Integer.valueOf(split[1]), DateUtil.isLeapYear(Integer.valueOf(split[0])));
        return (null !=userNum?userNum:0)/lengthOfMonth;
    }


    /**
     * 安全管理查询
     */
    @Override
    public List<ReportDetailDTO> findSafe(Integer reportId) {
        //查询上月数据
        List<ReportDetailDTO> list = new ArrayList<>();
        //查询ai
        ReportDetailDTO aiDetailDTO = reportAiService.dataHandler(reportId, "AI识别",null);
        if (null != aiDetailDTO){
            list.add(aiDetailDTO);
        }
        ReportDetailDTO elecDataHandler = reportElectricalService.dataHandler(reportId, "电气火灾监测",null);
        if (null != elecDataHandler){
            list.add(elecDataHandler);
        }
        return list;
    }

    /**
     * 大型作业设备查询
     */
    @Override
    public List<ReportDetailDTO> findLargeDevice(Integer reportId) {
        //查询上月数据
        List<ReportDetailDTO> list = new ArrayList<>();
        //查询塔吊
        ReportDetailDTO aiDetailDTO = reportMachineryService.dataHandler(reportId, "塔吊监测",1);
        if (null != aiDetailDTO){
            list.add(aiDetailDTO);
        }
        ReportDetailDTO elecDataHandler = reportMachineryService.dataHandler(reportId, "升降机监测",2);
        if (null != elecDataHandler){
            list.add(elecDataHandler);
        }
        return list;
    }

    /**
     * 喷淋控制
     * @param reportId
     * @param result
     */
    private void sprayHandler(Integer reportId, List<ReportDetailDTO> result) {
        QueryWrapper<ReportSpray> sprayQueryWrapper = new QueryWrapper<>();
        sprayQueryWrapper.eq("report_id_", reportId);
        List<ReportSpray> reportSprays = reportSprayMapper.selectList(sprayQueryWrapper);
        //求人均
        List<ReportSprayVo> sprayVos = BeanUtil.copyToList(reportSprays, ReportSprayVo.class);

        //设备名称
        List<String> codes = sprayVos.stream().filter(s -> StringUtils.isNotBlank(s.getDeviceCode())).map(l -> l.getDeviceCode()).collect(Collectors.toList());
        QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("code",codes);
        List<Device> deviceList = deviceMapper.selectList(queryWrapper);
        sprayVos.forEach(s->{
            if (CollectionUtil.isNotEmpty(deviceList)){
                List<Device> devices = deviceList.stream().filter(d -> d.getCode().equals(s.getDeviceCode())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(devices)){
                    s.setDeviceName(devices.get(0).getName());
                }
            }
            if (null != s.getDay() && null != s.getHours()){
                s.setDayHours(0==s.getDay()?0d:(s.getHours()/s.getDay()));
                if (s.getDayHours() != 0){
                    s.setDayHours(new BigDecimal(s.getHours()/s.getDay()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                }
            }
        });
        ReportDetailDTO reportDetailDTO = new ReportDetailDTO<>();
        reportDetailDTO.setName("喷淋控制");
        reportDetailDTO.setItem(sprayVos);
        result.add(reportDetailDTO);
    }

    /**
     * 用水用电处理
     * @param userNum
     * @param lastUserNum
     * @param curEnergyList
     * @param lastEnergyList
     * @param type
     */
    private void energyHandler(Integer userNum, Integer lastUserNum, List<ReportEnergy> curEnergyList, List<ReportEnergy> lastEnergyList, Integer type,List<ReportDetailDTO> result) {
        Map<String,Object> map = new HashMap<>();
        List<ReportEnergy> curList = curEnergyList.stream().filter(e -> type.equals(e.getType())).collect(Collectors.toList());
        List<ReportEnergy> lastList = lastEnergyList.stream().filter(e -> type.equals(e.getType())).collect(Collectors.toList());
        List<ReportCountDTO<Double>> list = new ArrayList<>();
        //本月检测天数
        AtomicInteger curWatchDay = new AtomicInteger();
        AtomicInteger lastWatchDay = new AtomicInteger();
        if (CollectionUtil.isNotEmpty(curList)){
            //根据deviceCode合并本月和上个月设备code
            List<String> deviceCodes = Stream.of(curList, lastList).flatMap(Collection::stream)
                    .map(r -> r.getDeviceCode()).collect(Collectors.toSet()).stream().collect(Collectors.toList());
            deviceCodes.sort(Comparator.nullsLast(String::compareTo));
            deviceCodes.forEach(deviceCode ->{
                ReportEnergy cur;
                ReportEnergy last;
                //判断是不是null
                if (StringUtils.isNotBlank(deviceCode)){
                    cur = curList.stream().filter(c -> deviceCode.equals(c.getDeviceCode())).findFirst().orElse(new ReportEnergy());
                    last = lastList.stream().filter(c -> deviceCode.equals(c.getDeviceCode())).findFirst().orElse(new ReportEnergy());
                }else {
                    cur = curList.stream().filter(c -> StringUtils.isBlank(c.getDeviceCode())).findFirst().orElse(new ReportEnergy());
                    last = lastList.stream().filter(c -> StringUtils.isBlank(c.getDeviceCode())).findFirst().orElse(new ReportEnergy());
                    curWatchDay.set(null != cur.getDay()?cur.getDay():0);
                    lastWatchDay.set(null != last.getDay()?last.getDay():0);
                }
                ReportCountDTO<Double> reportCountDTO = new ReportCountDTO<>(deviceCode,cur.getNum(),last.getNum());
                list.add(reportCountDTO);
            });
            //组织结果返回
            //本月监测天数
            map.put("curWatchDay",curWatchDay.get());
            //本月总量
            ReportCountDTO<Double> countDTO = list.stream().filter(l -> StringUtils.isBlank(l.getName())).findFirst().orElse(new ReportCountDTO<>());
            map.put("sum",countDTO.getCurrentMonth());
            //环比
            map.put("sumRatio",countDTO.getRatio());
            //平均在场人数
            map.put("userNum",userNum);
            //人均用量
            Double userAvg = avgEnergy(countDTO.getCurrentMonth(), userNum.doubleValue(), new Double(curWatchDay.get()));
            map.put("userAvg",userAvg);

            //对比
            Double lastUserAvg = avgEnergy(countDTO.getLastMonth(), lastUserNum.doubleValue(), new Double(lastWatchDay.get()));
            map.put("avgRatio",ReportCountDTO.calRatio(userAvg,lastUserAvg));
            //列表
            //求设备名称
            if (CollectionUtil.isNotEmpty(list)){
                List<String> codes = list.stream().filter(l -> StringUtils.isNotBlank(l.getName())).map(l -> l.getName()).collect(Collectors.toList());
                QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
                queryWrapper.in("code",codes);
                List<Device> deviceList = deviceMapper.selectList(queryWrapper);
                list.forEach(l->{
                    List<Device> devices = deviceList.stream().filter(d -> d.getCode().equals(l.getName())).collect(Collectors.toList());
                    if (CollectionUtil.isNotEmpty(devices)){
                        l.setName(devices.get(0).getName());
                    }
                });
            }
            map.put("list",list);

            //汇总
            ReportDetailDTO reportDetailDTO = new ReportDetailDTO<>();
            reportDetailDTO.setName(Integer.valueOf(1).equals(type)?"智能水表":"智能电表");
            reportDetailDTO.setItem(map);
            result.add(reportDetailDTO);
        }
    }

    /**
     * 项目报告月份平均每天在场人数
     */
    private Integer getUserNum(Integer reportId){
        Integer userNum = userStatisticsMapper.findNumByReportId(reportId);
        return null != userNum?userNum:0;
    }

    /**
     * 计算人均用量
     */
    private Double avgEnergy(Double sumEnergy,Double userNum,Double dayNum){
        double result = 0;
        if ((null != userNum && userNum > 0) && (null != dayNum && dayNum > 0)){
            result = sumEnergy/userNum/dayNum;
        }
        return new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}
